/* file: code.c */

#include "public.h"
#include "parser.h"
#include "code.h"
#include "tools.h"

static void stmt_gene(parser_tree*);
static void exp_gene(parser_tree*);

void code_gene(parser_tree *tree)
{
	if(tree == NULL)
		return;
	switch(tree -> base)
	{
		case stmt_b:
			stmt_gene(tree);
			break;
		case exp_b:
			exp_gene(tree);
			break;
		default:
			break;
	}
	code_gene(tree -> next);                               /* analyze the next statement */
}

static void stmt_gene(parser_tree *tree)
{
	int data_addr;                                            /* data segment address */
	int addr1_save, addr2_save, curr_addr_saved;              /* code segment address */
	switch((tree -> kind).stmt)
	{
		case null_s:
			write_null("nop");
			break;
		case in_s:
			write_in();
			data_addr = find_tab((tree -> attribute).name);
			write_mov_mem(data_addr, "ax");
			break;
		case out_s:
			data_addr = find_tab((tree -> attribute).name);
			write_mov_reg("ax", data_addr);
			write_out();
			break;
		case assign_s:
			code_gene(tree -> child[0]);
			data_addr = find_tab((tree -> attribute).name);
			write_mov_mem(data_addr, "ax");
			break;
		case while_s:
			save_curr_addr(&addr1_save);
			code_gene(tree -> child[0]);
			save_curr_addr(&addr2_save);
			skip_curr_addr();
			code_gene(tree -> child[1]);
			write_jmp("jmp", addr1_save);
			save_curr_addr(&curr_addr_saved);
			back_patch(addr2_save);
			write_jmp("jeq", curr_addr_saved);
			back_patch(curr_addr_saved);         /* restore current address */
		case if_s:
			code_gene(tree -> child[0]);         /* generate code of if_s's first child*/
			save_curr_addr(&addr1_save);
			skip_curr_addr();

			code_gene(tree -> child[1]);
			save_curr_addr(&addr2_save);
			skip_curr_addr();
			save_curr_addr(&curr_addr_saved);
			back_patch(addr1_save);
			write_jmp("jeq", curr_addr_saved); 
			back_patch(curr_addr_saved);

			code_gene(tree -> child[2]);
			save_curr_addr(&curr_addr_saved);
			back_patch(addr2_save);
			write_jmp("jmp", curr_addr_saved);
			back_patch(curr_addr_saved);           /*  restore current address */
		default:
			break;

	}
}

static void exp_gene(parser_tree *tree)
{
	int data_addr;
	int curr_addr;
	switch((tree -> kind).exp)
	{
		case id_e:
			data_addr = find_tab((tree -> attribute).name);
			write_mov_reg("ax", data_addr);
			break;
		case const_e:
			mov_ax_num((tree -> attribute).value);
			break;
		case op_e:
			code_gene(tree ->child[0]);
			write_push();
			code_gene(tree -> child[1]);
			write_pop();
			write_swap();
			switch((tree -> attribute).op)
			{
				case PLUS:
					write_cal("add");
					break;
				case MINUS:
					write_cal("sub");
					break;
				case TIMES:
					write_cal("mul");
					break;
				case DIVI:
					write_cal("div");
					break;
				case LS:
					write_cal("sub");
					save_curr_addr(&curr_addr);
					write_jmp("jls", curr_addr + 3);
					mov_ax_num(0);
					/*
					   write_mov_reg("ax", 0);*/
					write_jmp("jmp", curr_addr + 4);
					mov_ax_num(1);
					/*
					   write_mov_reg("ax", 1);*/
					break;
				case LE:
					write_cal("sub");
					save_curr_addr(&curr_addr);
					write_jmp("jle", curr_addr + 3);
					mov_ax_num(0);
					/*
					   write_mov_reg("ax", 0);*/
					write_jmp("jmp", curr_addr + 4);
					mov_ax_num(1);
					/*
					   write_mov_reg("ax", 1);*/
					break;
				case GT:
					write_cal("sub");
					save_curr_addr(&curr_addr);
					write_jmp("jgt", curr_addr + 3);
					mov_ax_num(0);
					/*
					   write_mov_reg("ax", 0);*/
					write_jmp("jmp", curr_addr + 4);
					mov_ax_num(1);
					/*
					   write_mov_reg("ax", 1);*/
					break;
				case GE:
					write_cal("sub");
					save_curr_addr(&curr_addr);
					write_jmp("jge", curr_addr + 3);
					mov_ax_num(0);
					/*
					   write_mov_reg("ax", 0);*/
					write_jmp("jmp", curr_addr + 4);
					mov_ax_num(1);
					/*
					   write_mov_reg("ax", 1);*/
					break;
				case EQ:
					write_cal("sub");
					save_curr_addr(&curr_addr);
					write_jmp("jeq", curr_addr + 3);
					mov_ax_num(0);
					/*
					   write_mov_reg("ax", 0);*/
					write_jmp("jmp", curr_addr + 4);
					mov_ax_num(1);
					/*
					   write_mov_reg("ax", 1);*/
					break;
				case NE:
					write_cal("sub");
					save_curr_addr(&curr_addr);
					write_jmp("jne", curr_addr + 3);
					mov_ax_num(0);
					/*
					   write_mov_reg("ax", 0);*/
					write_jmp("jmp", curr_addr + 4);
					mov_ax_num(1);
					/*
					   write_mov_reg("ax", 1);*/
					break;
				default:
					break;
			}
		default:
			break;
	}
}
